
#ifndef __pixeltypeH__
#define __pixeltypeH__

#include "graphics.h"

namespace WtfGraphics {
	template <typename tElem, int Dim, int iType>
	class PixelBuffer;

	namespace PixelType {
		typedef enum {
			RGBA8, RGBA32f, RGB8, RGB32f
		} tPixelType;
	};


	/**
	 * Pixel buffer interface - represents an array of pixels of arbritrary type, and
	 * supports conversions between those types.
	 **/
	class IPixelBuffer: public virtual WtfEngine::IGcObject {
		public:
			/// Returns number of pixels
			virtual int getSize() const = 0;
			/// Returns size in bytes
			virtual int getRawSize() const = 0;
			
			virtual PixelType::tPixelType getPixelType() const = 0;

			virtual void Destroy() = 0;


			GC_INTERFACE(IPixelBuffer);
	};

	template <typename tElem, int Dim, int iType>
	class PixelBuffer: public virtual IPixelBuffer, public WtfEngine::GcObject {
	public:
		typedef PixelBuffer<tElem, Dim, iType>	tThis;
		typedef typename VectorType<tElem, Dim>::tType tPixelType;
		enum { Type = iType };

		int				mNumPixels;
		tPixelType *	mpPixels;
		bool			mbOwnsBuffer;

	public:
		PixelBuffer(): WtfEngine::GcObject(), mNumPixels(0), mpPixels(NULL), mbOwnsBuffer(false) {};

		/// Allocates a new pixel buffer of the specified size
		explicit PixelBuffer(int numPixels): WtfEngine::GcObject(), mNumPixels(numPixels) {
			mpPixels = new tPixelType[numPixels];
			mbOwnsBuffer = true;
		};

		/// Construct from a preallocated pixel buffer.
		/// @warning Ensure that the pixel buffer is of the correct format!
		PixelBuffer(int numPixels, void * pPixels): WtfEngine::GcObject(), mNumPixels(numPixels),
			mpPixels((tPixelType *) pPixels), mbOwnsBuffer(false) {};

		/// Copy constructor - creates a copy of the buffer
		PixelBuffer(const tThis& pb): WtfEngine::GcObject(), mNumPixels(pb.mNumPixels), mpPixels(NULL) {
			(*this) = pb;
		};

		/// Assignment - to keep ownership of the pointer
		void operator = (const tThis& pb) {
			Destroy();
			mNumPixels = pb.mNumPixels;
			mpPixels = new tPixelType[mNumPixels];
			mbOwnsBuffer = true;
			
			memcpy(mpPixels, pb.mpPixels, getRawSize());
		};

		virtual ~PixelBuffer() {
			Destroy();
		};


		/// Returns number of pixels
		virtual int getSize() const {
			return mNumPixels;
		};
		/// Returns size in bytes
		virtual int getRawSize() const {
			return (mNumPixels * sizeof(tPixelType));
		};
		
		/// Access to pixels as raw array
		inline tElem * getPixels() {
			return reinterpret_cast<tElem *>(mpPixels);
		};
		inline const tElem * getPixels() const {
			return reinterpret_cast<const tElem *>(mpPixels);
		};

		/// Access to pixels (as vectors)
		inline tPixelType& operator [] (int i) {
			assert(0 <= i && i < mNumPixels);
			return mpPixels[i];
		};
		inline const tPixelType& operator [] (int i) const {
			assert(0 <= i && i < mNumPixels);
			return mpPixels[i];
		};

		inline PixelType::tPixelType getPixelType() const {
			return (PixelType::tPixelType) Type;
		};

		virtual void Destroy() {
			if(mbOwnsBuffer) {
				delete mpPixels;
				mbOwnsBuffer = false;
			};

			mpPixels = NULL; mNumPixels = 0;
		};

		GC_OBJECT(tThis, sizeof(tThis) + getRawSize());

		/// Conversion from arbritary pixel types
		static tRef UpcastRef(const IPixelBuffer::tRef& rPxBuf);
	};

	/// Some common pixel types
	namespace PixelType {
		typedef PixelBuffer<unsigned char, 3, RGB8>		tRGB8;
		typedef PixelBuffer<float, 3, RGB32f>			tRGB32f;
		typedef PixelBuffer<unsigned char, 4, RGBA8>	tRGBA8;
		typedef PixelBuffer<float, 4, RGBA32f>			tRGBA32f;
	};
};

#endif
